package com.xinghai.system.service.impl;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.aliyun.dingtalkcontact_1_0.models.GetUserHeaders;
import com.aliyun.dingtalkoauth2_1_0.models.GetUserTokenRequest;
import com.aliyun.dingtalkoauth2_1_0.models.GetUserTokenResponse;
import com.aliyun.dingtalkrobot_1_0.Client;
import com.aliyun.dingtalkrobot_1_0.models.BatchSendOTOHeaders;
import com.aliyun.dingtalkrobot_1_0.models.BatchSendOTORequest;
import com.aliyun.dingtalkrobot_1_0.models.BatchSendOTOResponse;
import com.aliyun.dingtalktodo_1_0.models.CreateTodoTaskHeaders;
import com.aliyun.dingtalktodo_1_0.models.CreateTodoTaskRequest;
import com.aliyun.dingtalktodo_1_0.models.CreateTodoTaskResponse;
import com.aliyun.dingtalkworkflow_1_0.models.*;
import com.aliyun.tea.TeaException;
import com.aliyun.teaopenapi.models.Config;
import com.aliyun.teautil.Common;
import com.aliyun.teautil.models.RuntimeOptions;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.dingtalk.api.DefaultDingTalkClient;
import com.dingtalk.api.DingTalkClient;
import com.dingtalk.api.request.OapiV2UserGetRequest;
import com.dingtalk.api.response.OapiV2UserGetResponse;
import com.xinghai.common.annotation.Excel;
import com.xinghai.common.core.domain.entity.SysDept;
import com.xinghai.common.core.domain.entity.SysDictData;
import com.xinghai.common.core.domain.entity.SysUser;
import com.xinghai.common.core.redis.RedisCache;
import com.xinghai.common.enums.DingdingRowNameEnum;
import com.xinghai.common.enums.ReflexaEnum;
import com.xinghai.common.enums.WorkFlowEnum;
import com.xinghai.common.utils.DateUtils;
import com.xinghai.common.utils.SecurityUtils;
import com.xinghai.common.utils.StringUtils;
import com.xinghai.common.utils.file.FileUploadUtils;
import com.xinghai.common.utils.http.HttpUtils;
import com.xinghai.system.domain.*;
import com.xinghai.system.domain.dto.*;
import com.xinghai.system.domain.vo.*;
import com.xinghai.system.mapper.DingUserMapper;
import com.xinghai.system.service.*;
import com.xinghai.system.util.BeanMapperUtils;
import com.xinghai.system.util.MoneyUtils;
import com.xinghai.system.util.PDFUtils;
import com.xinghai.system.util.XHConstant;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.Lists;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.http.*;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ResourceUtils;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.PostConstruct;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Slf4j
@Service
public class DingdingOaServiceImpl implements DingdingOaService {

    @Autowired
    private IXhUseVehicleInfoService xhUseVehicleInfoService;

    @Autowired
    private IXhUseVehicleInfoDetailsService xhUseVehicleInfoDetailsService;

    @Autowired
    private ISysDeptService sysDeptService;

    @Autowired
    private ISysNoticeService noticeService;

    /* 钉钉组织架构服务 BEGIN*/
    @Autowired
    private IDingDeptService dingDeptService;

    @Autowired
    private IDingUserService dingUserService;
    /* 钉钉组织架构服务 END*/

    @Autowired
    private ISysDictDataService sysDictDataService;

    @Autowired
    private DingUserMapper dingUserMapper;

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private RedisCache redisCache;
    @Autowired
    private IXhVehicleOrderInfoService xhVehicleOrderInfoService;
    @Autowired
    private IXhVehicleOrderHisService xhVehicleOrderHisService;
    @Autowired
    private IXhVehicleInfoMaterialService materialService;
    @Autowired
    private IXhCarInfoService carInfoService;
    @Autowired
    private IXhCarTypeInfoService carTypeInfoService;
    @Autowired
    private IXhDriverInfoService driverInfoService;
    @Autowired
    private IXhContractInfoService contractInfoService;
    @Autowired
    private IXhVehicleOrderFeeService xhVehicleOrderFeeService;
    @Autowired
    private IXhVehicleOrderCarHisService carHisService;
    @Autowired
    private ISysUserService userService;

    @Value("${dingtalk.contract.url}")
    private String downloadUrl;

    private String url;

    @Value("${dingtalk.appkey}")
    private String appkey;

    @Value("${dingtalk.appsecre}")
    private String appsecret;

    @Value("${dingtalk.wlgs}")
    private String wlgs;

    @Value("${dingtalk.robotCode}")
    private String robotCode;

    private Client robotClient;

    @Value("${dingtalk.processCode}")
    private String processCode;

    private static final String pdfName = "兰剑货物托运协议.pdf";


    @PostConstruct
    public void init() throws Exception {
        Config config = new Config();
        config.protocol = "https";
        config.regionId = "central";
        robotClient = new Client(config);
    }


    public static com.aliyun.dingtalkoauth2_1_0.Client authClient() throws Exception {
        Config config = new Config();
        config.protocol = "https";
        config.regionId = "central";
        return new com.aliyun.dingtalkoauth2_1_0.Client(config);
    }

    public static com.aliyun.dingtalkcontact_1_0.Client contactClient() throws Exception {
        Config config = new Config();
        config.protocol = "https";
        config.regionId = "central";
        return new com.aliyun.dingtalkcontact_1_0.Client(config);
    }


    @Override
    public String getToken() {
        String resStr = HttpUtils.sendGet("https://oapi.dingtalk.com/gettoken", "appkey=" + appkey + "&appsecret=" + appsecret);
        if (StringUtils.isBlank(resStr)) {
            return null;
        }
        return JSONObject.parseObject(resStr).getString("access_token");
    }

    @Override
    public void addComments(Long id) throws Exception {
        XhUseVehicleInfoDetails entityByInfoId = xhUseVehicleInfoDetailsService.getEntityByInfoId(id);
        log.info(JSONObject.toJSONString(entityByInfoId));
        url = "https://api.dingtalk.com/v1.0/workflow/processInstances/comments";
        XhUseVehicleInfo info = xhUseVehicleInfoService.getInfo(id);
        if (Objects.nonNull(info)) {
            String instanid = info.getRemark();
//            HttpHeaders headers = new HttpHeaders();
//            headers.setContentType(MediaType.APPLICATION_JSON);
//            headers.set("x-acs-dingtalk-access-token",getToken());
//            Map<String, Object> paramMap = new HashMap<>();
//            paramMap.put("photos",Arrays.asList(entityByInfoId.getDingtalkImgLinks()));
//            // 请求体
//            Map<String, String> requestBody = new HashMap<>();
//            requestBody.put("processInstanceId", instanid);
//            requestBody.put("text", entityByInfoId.getComments());
//            requestBody.put("commentUserId", "25503248011229522");
//            requestBody.put("file", JSONObject.toJSONString(paramMap));
//            log.info("[httpRequest][请求参数({})]", requestBody);
//            // 发送请求
//            HttpEntity<Map<String, String>> requestEntity = new HttpEntity<>(requestBody, headers);
//            ResponseEntity<String> responseEntity = this.restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class);

            com.aliyun.dingtalkworkflow_1_0.Client client = this.createClient();
            AddProcessInstanceCommentHeaders addProcessInstanceCommentHeaders = new AddProcessInstanceCommentHeaders();
            addProcessInstanceCommentHeaders.xAcsDingtalkAccessToken = getToken();


            String dingtalkImgLinks = entityByInfoId.getDingtalkImgLinks();
            AddProcessInstanceCommentRequest.AddProcessInstanceCommentRequestFile file = new AddProcessInstanceCommentRequest.AddProcessInstanceCommentRequestFile();
            entityByInfoId.getDingtalkImgLinks();
            if (StringUtils.isNotBlank(dingtalkImgLinks)) {
                file.setPhotos(Arrays.asList(
                        dingtalkImgLinks.split(",")
                ));
            }
            AddProcessInstanceCommentRequest addProcessInstanceCommentRequest = new AddProcessInstanceCommentRequest()
                    .setProcessInstanceId(instanid)
                    .setText(entityByInfoId.getComments())
                    .setCommentUserId(wlgs);
            if (Objects.nonNull(file.getPhotos())) {
                addProcessInstanceCommentRequest.setFile(file);
            }
            try {
                client.addProcessInstanceCommentWithOptions(addProcessInstanceCommentRequest, addProcessInstanceCommentHeaders, new RuntimeOptions());
            } catch (TeaException err) {
                if (!Common.empty(err.code) && !Common.empty(err.message)) {
                    // err 中含有 code 和 message 属性，可帮助开发定位问题
                }

            } catch (Exception _err) {
                TeaException err = new TeaException(_err.getMessage(), _err);
                if (!Common.empty(err.code) && !Common.empty(err.message)) {
                    // err 中含有 code 和 message 属性，可帮助开发定位问题
                }

            }
//            log.debug("[responseEntity][的响应结果({})]", responseEntity);
        }

    }

    @Override
    public void addNotice(String str, Boolean isBack) {
        try {
            log.info("发送站内信实例ID为：{}", str);
            JSONObject jsonObject = JSONObject.parseObject(str);
            //实例ID
            String processInstanceId = jsonObject.getString("processInstanceId");
            String remark = jsonObject.getString("remark");
            String useIds = processInstances(processInstanceId);
            if (StringUtils.isNotBlank(useIds)) {
                getSysNotice(processInstanceId, useIds, isBack, remark);
            }
        } catch (Exception e) {
            log.error(e.getMessage());
        }
    }

    public void getSysNotice(String processInstanceId, String useIds, boolean isBack, String remark) {
        if (useIds.contains(wlgs)) {
            SysDept sysDept = (SysDept) redisCache.getAndRotateFirstItem("logisticsCompanies");
            Long deptId = sysDept.getDeptId();
            //包含物流公司审核节点
            XhUseVehicleInfo one = xhUseVehicleInfoService.getOne(Wrappers.<XhUseVehicleInfo>lambdaQuery().eq(XhUseVehicleInfo::getRemark, processInstanceId));

            if(Objects.nonNull(one)){
                //查询当前物流公司的账号
                LambdaQueryWrapper<SysUser> eq = Wrappers.<SysUser>lambdaQuery().eq(SysUser::getDeptId, deptId).eq(SysUser::getStatus,StringPool.ZERO);
                List<SysUser> list = userService.list(eq);
                if(CollectionUtils.isNotEmpty(list)){
                    List<SysNotice> addList = Lists.newArrayList();
                    list.forEach(
                            it->{
                                StringBuilder sb = new StringBuilder();
                                sb.append("项目名称【");
                                sb.append(one.getProjectName());
                                sb.append("】的消息待办请及时处理！");
                                SysNotice sysNotice = new SysNotice();
                                if (isBack) {
                                    sysNotice.setNoticeTitle("您有退回的待办申请需要处理！");
                                    sb.append("对方反馈信息:[");
                                    sb.append(remark);
                                    sb.append("]");
                                } else {
                                    sysNotice.setNoticeTitle("您有一条待办需要处理！");
                                }
                                sysNotice.setNoticeContent(sb.toString());
                                sysNotice.setNoticeType(StringPool.ONE);
                                sysNotice.setStatus(StringPool.ZERO);
                                sysNotice.setCreateTime(new Date());
                                sysNotice.setUpdateTime(new Date());
                                sysNotice.setCreateBy(it.getUserName());
                                sysNotice.setInstanceId(processInstanceId);
                                log.info(JSONObject.toJSONString(sysNotice));
                                addList.add(sysNotice);
                            }
                    );

                    //批量保存
                    noticeService.saveBatch(addList);
                }
            }

        }
    }

    private String getDistLabel(Long dictCode) {
        SysDictData sysDictData = sysDictDataService.selectDictDataById(dictCode);
        return sysDictData.getDictLabel();
    }

    @Override
    public String createFlow(Long id) throws MalformedURLException {
        //获取用车信息
        XhUseVehicleInfo info = xhUseVehicleInfoService.getInfo(id);
        XhUseVehicleInfoDto dto = new XhUseVehicleInfoDto();
        BeanUtils.copyProperties(info, dto);
        //设备类型
        String eqType = getDistLabel(info.getEqType());
        dto.setEqType(eqType);
        //发货方式
        //发货时间
//        String deliverDate = info.getDeliverDate().toInstant()
//                .atZone(ZoneId.systemDefault())
//                .toLocalDate().format(DateTimeFormatter.ofPattern("YYYY-MM-dd")).toString();
//        dto.setDeliverDate(deliverDate);
        dto.setDeliverDate(info.getDeliverDate());
//        //到货时间
//        String needGiveDate = info.getNeedGiveDate().toInstant()
//                .atZone(ZoneId.systemDefault())
//                .toLocalDate().format(DateTimeFormatter.ofPattern("YYYY-MM-dd")).toString();
//        dto.setNeedGiveDate(needGiveDate);
        dto.setNeedGiveDate(info.getNeedGiveDate());
        //需求车型
        String needVehicle = info.getNeedVehicle();
        dto.setNeedVehicle(needVehicle);
        //打包图片
        dto.setImgLinks(JSONObject.toJSONString(Arrays.asList(info.getImgLinks().split(","))));
        //是否异常费用
        String isAbnormalSum = info.getIsAbnormalSum();
        if ("1".equals(isAbnormalSum)) {
            dto.setIsAbnormalSum("是");
        } else {
            dto.setIsAbnormalSum("否");
        }
        //所在部门
        dto.setDept(sysDeptService.selectDeptById(info.getDeptId()).getDeptName());

        String url = "https://api.dingtalk.com/v1.0/workflow/processInstances";

        String[] fieldNames = {
                "项目名称", "项目经理", "设备类型", "始发地", "目的地",
                "发货方式", "发货原因", "打包图片", "要求发货时间", "要求到货时间",
                "预估需求车型", "公里数（公里）", "预估装载率", "装车人员", "发货名称",
                "发货设备入库单号", "发货设备移库单号", "是否产生异常费用", "所在部门"
        };

        String[] getterMethods = {
                "getProjectName", "getProjectManager", "getEqType", "getBeginAddr", "getEndAddr",
                "getDeliveryWay", "getDeliverDesc", "getImgLinks", "getDeliverDate", "getNeedGiveDate",
                "getNeedVehicle", "getNeedVehicle", "getEstimateLoadRate", "getPuckVehPeople", "getShippingName",
                "getPushStashNo", "getChangeStashNo", "getIsAbnormalSum", "getDept"
        };

        List<Map<String, Object>> mapList = Lists.newArrayList();
        for (int i = 0; i < fieldNames.length; i++) {
            Map<String, Object> map = new HashMap<>();
            map.put("name", fieldNames[i]);
            try {
                Method method = XhUseVehicleInfoDto.class.getMethod(getterMethods[i]);
                Object value = method.invoke(dto);
                map.put("value", value);
            } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
                System.err.println("Error getting value for field: " + fieldNames[i]);
            }
            mapList.add(map);
        }


//        paramMap.put("deptId", "-1");
//        paramMap.put("processCode", "");
//        paramMap.put("originatorUserId", "25503248011229522");
//        paramMap.put("formComponentValues",mapList);

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        headers.set("x-acs-dingtalk-access-token", getToken());
        // 请求体
        Map<String, String> requestBody = new HashMap<>();
        requestBody.put("deptId", "-1");
        requestBody.put("processCode", processCode);
        requestBody.put("originatorUserId", "294635133825887650");
        requestBody.put("formComponentValues", JSONObject.toJSONString(mapList));
        log.info("[httpRequest][请求参数({})]", requestBody);
        // 发送请求
        HttpEntity<Map<String, String>> requestEntity = new HttpEntity<>(requestBody, headers);
        ResponseEntity<String> responseEntity = this.restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class);
        log.debug("[httpRequest][的响应结果({})]", responseEntity);
        String instanceId = JSONObject.parseObject(responseEntity.getBody()).getString("instanceId");
        XhUseVehicleInfo xhUseVehicleInfo = new XhUseVehicleInfo();
        xhUseVehicleInfo.setId(id);
        xhUseVehicleInfo.setRemark(instanceId);
        int update = xhUseVehicleInfoService.update(xhUseVehicleInfo);
        return instanceId;
    }

    /**
     * 使用 Token 初始化账号Client
     *
     * @return Client
     * @throws Exception
     */
    private com.aliyun.dingtalkworkflow_1_0.Client createClient() throws Exception {
        Config config = new Config();
        config.protocol = "https";
        config.regionId = "central";
        return new com.aliyun.dingtalkworkflow_1_0.Client(config);
    }

    public static com.aliyun.dingtalktodo_1_0.Client createTodoClient() throws Exception {
        Config config = new Config();
        config.protocol = "https";
        config.regionId = "central";
        return new com.aliyun.dingtalktodo_1_0.Client(config);
    }

    @Override
    public boolean approvalFlow(DingApprovalResDTO dingApprovalResDTO) {
        try {
            //查询流程详情中，当前审批节点，从而提取该节点审批人以及审批任务id
            List<GetProcessInstanceResponseBody.GetProcessInstanceResponseBodyResultTasks> tasksList = this.getProcessTaskList(dingApprovalResDTO.getProcessInstanceId());
            if (tasksList.size() == 0) {
                throw new Exception("请选择审批流程");
            }
            //过滤当前进程中，需要审批的节点
            GetProcessInstanceResponseBody.GetProcessInstanceResponseBodyResultTasks task = tasksList.stream().filter(f -> f.getStatus().equals(WorkFlowEnum.DING_RUNNING.getCode())).collect(Collectors.toList()).get(0);
            dingApprovalResDTO.setTaskId(task.getTaskId());
            dingApprovalResDTO.setActionerUserId(task.getUserId());

            //执行操作
            com.aliyun.dingtalkworkflow_1_0.Client client = this.createClient();
            ExecuteProcessInstanceHeaders executeProcessInstanceHeaders = new ExecuteProcessInstanceHeaders();
            executeProcessInstanceHeaders.xAcsDingtalkAccessToken = getToken();
            //附件文件信息
//            com.aliyun.dingtalkworkflow_1_0.models.ExecuteProcessInstanceRequest.ExecuteProcessInstanceRequestFileAttachments fileAttachments0 = new com.aliyun.dingtalkworkflow_1_0.models.ExecuteProcessInstanceRequest.ExecuteProcessInstanceRequestFileAttachments()
//                    .setSpaceId("123")
//                    .setFileSize("1024")
//                    .setFileId("B1oQixxxx")
//                    .setFileName("文件名称。")
//                    .setFileType("file");
            //拼接请求参数
            ExecuteProcessInstanceRequest.ExecuteProcessInstanceRequestFile file = new ExecuteProcessInstanceRequest.ExecuteProcessInstanceRequestFile()
                    .setPhotos(dingApprovalResDTO.getPhotos());
            ExecuteProcessInstanceRequest executeProcessInstanceRequest = new ExecuteProcessInstanceRequest()
                    .setProcessInstanceId(dingApprovalResDTO.getProcessInstanceId())
                    .setRemark(dingApprovalResDTO.getRemark())
                    .setResult(dingApprovalResDTO.getResult())
                    .setActionerUserId(dingApprovalResDTO.getActionerUserId())
                    .setTaskId(dingApprovalResDTO.getTaskId())
                    .setFile(file);
            log.debug("[httpRequest][请求参数({})]", executeProcessInstanceRequest);
            ExecuteProcessInstanceResponse response = client.executeProcessInstanceWithOptions(executeProcessInstanceRequest, executeProcessInstanceHeaders, new RuntimeOptions());
            SysNotice notice = noticeService.getSysNoticeByInstanceId(dingApprovalResDTO.getProcessInstanceId());
            if (Objects.nonNull(notice)) {
                //CLOSE NOTICE
                notice.setStatus(StringPool.ONE);
                noticeService.updateNotice(notice);
            }
            //更新审批状态
            xhVehicleOrderInfoService.updateStatusByProcessInstanceId(dingApprovalResDTO.getProcessInstanceId(),XHConstant.ORDER_STATUS_PROCESS);
        } catch (TeaException err) {
            if (!Common.empty(err.code) && !Common.empty(err.message)) {
                // err 中含有 code 和 message 属性，可帮助开发定位问题
                log.debug("[httpRequest][返回异常_Tea({})]", err.message);
                return false;
            }

        } catch (Exception _err) {
            TeaException err = new TeaException(_err.getMessage(), _err);
            if (!Common.empty(err.code) && !Common.empty(err.message)) {
                // err 中含有 code 和 message 属性，可帮助开发定位问题
                log.debug("[httpRequest][返回异常_Exe({})]", err.message);
                return false;
            }
        }
        return true;
    }

    private List<GetProcessInstanceResponseBody.GetProcessInstanceResponseBodyResultTasks> getProcessTaskList(String processInstanceId) throws Exception {
        List<GetProcessInstanceResponseBody.GetProcessInstanceResponseBodyResultTasks> tasksList = new ArrayList<>();
        com.aliyun.dingtalkworkflow_1_0.Client client = this.createClient();
        GetProcessInstanceHeaders getProcessInstanceHeaders = new GetProcessInstanceHeaders();
        getProcessInstanceHeaders.xAcsDingtalkAccessToken = this.getToken();
        GetProcessInstanceRequest getProcessInstanceRequest = new GetProcessInstanceRequest()
                .setProcessInstanceId(processInstanceId);
        try {
            GetProcessInstanceResponse response = client.getProcessInstanceWithOptions(getProcessInstanceRequest, getProcessInstanceHeaders, new RuntimeOptions());
            tasksList = response.getBody().getResult().getTasks();
        } catch (TeaException err) {
            if (!Common.empty(err.code) && !Common.empty(err.message)) {
                // err 中含有 code 和 message 属性，可帮助开发定位问题
            }
        } catch (Exception _err) {
            TeaException err = new TeaException(_err.getMessage(), _err);
            if (!Common.empty(err.code) && !Common.empty(err.message)) {
                // err 中含有 code 和 message 属性，可帮助开发定位问题
            }
        }
        return tasksList;
    }


    /**
     * 获取实例详情
     *
     * @return
     */
    public String processInstances(String instanceId) throws Exception {
        AtomicReference<String> userId = new AtomicReference<>();
        com.aliyun.dingtalkworkflow_1_0.Client client = this.createClient();
        GetProcessInstanceHeaders getProcessInstanceHeaders = new GetProcessInstanceHeaders();
        getProcessInstanceHeaders.xAcsDingtalkAccessToken = this.getToken();
        ;
        GetProcessInstanceRequest getProcessInstanceRequest = new GetProcessInstanceRequest()
                .setProcessInstanceId(instanceId);
        try {
            GetProcessInstanceResponse processInstanceWithOptions = client.getProcessInstanceWithOptions(getProcessInstanceRequest, getProcessInstanceHeaders, new RuntimeOptions());
            GetProcessInstanceResponseBody body = processInstanceWithOptions.getBody();
            if ("true".equals(body.getSuccess())) {
                GetProcessInstanceResponseBody.GetProcessInstanceResponseBodyResult result = body.getResult();
                Optional<GetProcessInstanceResponseBody.GetProcessInstanceResponseBodyResultTasks> running = result.getTasks().stream().filter(it -> WorkFlowEnum.DING_RUNNING.getCode().equals(it.getStatus())).findFirst();
                Optional<GetProcessInstanceResponseBody.GetProcessInstanceResponseBodyResultTasks> cancel = result.getTasks().stream().filter(it -> WorkFlowEnum.DING_CANCEL.getCode().equals(it.getStatus())).findFirst();
                if (!cancel.isPresent()) {
                    running.ifPresent(it -> {
                        userId.set(it.getUserId());
                    });
                }

                return userId.get();
            }

        } catch (TeaException err) {
            if (!Common.empty(err.code) && !Common.empty(err.message)) {
                // err 中含有 code 和 message 属性，可帮助开发定位问题
            }

        } catch (Exception _err) {
            TeaException err = new TeaException(_err.getMessage(), _err);
            if (!Common.empty(err.code) && !Common.empty(err.message)) {
                // err 中含有 code 和 message 属性，可帮助开发定位问题
            }

        }
        return null;
    }

    /**
     * 机器人发送
     *
     * @param text
     * @param userId
     * @return
     * @throws Exception
     */
    @Override
    public String send(String text, String userId) throws Exception {
        BatchSendOTOHeaders batchSendOTOHeaders = new BatchSendOTOHeaders();
        batchSendOTOHeaders.setXAcsDingtalkAccessToken(getToken());
        BatchSendOTORequest batchSendOTORequest = new BatchSendOTORequest();
        batchSendOTORequest.setMsgKey("sampleText");
        batchSendOTORequest.setRobotCode(robotCode);
        batchSendOTORequest.setUserIds(Arrays.asList(userId));

        JSONObject msgParam = new JSONObject();
        msgParam.put("content", text);
        batchSendOTORequest.setMsgParam(msgParam.toJSONString());

        try {
            BatchSendOTOResponse batchSendOTOResponse = robotClient.batchSendOTOWithOptions(batchSendOTORequest, batchSendOTOHeaders, new RuntimeOptions());
            if (Objects.isNull(batchSendOTOResponse) || Objects.isNull(batchSendOTOResponse.getBody())) {
                log.error("机器人回应返回错误, response={}",
                        batchSendOTOResponse);
                return null;
            }
            return batchSendOTOResponse.getBody().getProcessQueryKey();
        } catch (TeaException e) {
            log.error("RobotPrivateMessages_send batchSendOTOResponse throw TeaException, errCode={}, " +
                    "errorMessage={}", e.getCode(), e.getMessage(), e);
            throw e;
        } catch (Exception e) {
            log.error("RobotPrivateMessages_send batchSendOTOResponse throw Exception", e);
            throw e;
        }
    }

    @Override
    public List<DingTalkDeptVo> departmentList() {
        // 发送请求
        url = "https://oapi.dingtalk.com/topapi/v2/department/listsub";
        ResponseEntity<String> responseEntity = this.restTemplate.exchange(url + "?access_token=" + getToken(), HttpMethod.POST, null, String.class);
        log.debug("[httpRequest][的响应结果({})]", responseEntity);
        if (200 == responseEntity.getStatusCodeValue()) {
            JSONObject jsonObject = JSONObject.parseObject(responseEntity.getBody());
            String resultList = jsonObject.getString("result");
            List<DingTalkDeptVo> dingTalkDeptVos = JSONArray.parseArray(resultList, DingTalkDeptVo.class);
            List<DingTalkDeptVo> collect = dingTalkDeptVos.stream().collect(Collectors.toList());
            for (DingTalkDeptVo v : collect) {
               v.setChildren(getDeptListSub(v.getDept_id()));;
            }
            return dingTalkDeptVos;
        }
        return null;
    }

    public List<DingTalkDeptVo> getDeptListSub(String dept_id){
        String url = "https://oapi.dingtalk.com/topapi/v2/department/listsub";
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        // 请求体
        Map<String, String> requestBody = new HashMap<>();
        requestBody.put("dept_id", dept_id);
        requestBody.put("language", "zh_CN");
        log.info("[httpRequest][请求参数({})]", requestBody);
        HttpEntity<Map<String, String>> requestEntity = new HttpEntity<>(requestBody, headers);
        ResponseEntity<String> responseEntity = this.restTemplate.exchange(url + "?access_token=" + getToken(), HttpMethod.POST, requestEntity, String.class);
        log.debug("[httpRequest][的响应结果({})]", responseEntity);
        if (200 == responseEntity.getStatusCodeValue()) {
            JSONObject jsonObject = JSONObject.parseObject(responseEntity.getBody());
            String resultList = jsonObject.getString("result");
            List<DingTalkDeptVo> dingTalkDeptVos = JSONArray.parseArray(resultList, DingTalkDeptVo.class);
            for (DingTalkDeptVo dingTalkDeptVo : dingTalkDeptVos) {
                //递归取
                dingTalkDeptVo.setChildren(getDeptListSub(dingTalkDeptVo.getDept_id()));;
            }
            return dingTalkDeptVos;
        }
        return null;
    }

    @Override
    public List<DingTalkDeptVo> recursionDeptList(DingTalkDeptVo vo) {
        saveCollection(getUserDepartmentList(vo.getDept_id()));
        List<DingTalkDeptVo> children = vo.getChildren();
        if(CollectionUtils.isNotEmpty(children)){
            for (DingTalkDeptVo child : children) {
                saveCollection(getUserDepartmentList(child.getDept_id()));
            }
        }
        return Collections.emptyList();
    }

    @Override
    public List<DingTalkUserVo> getUserDepartmentList(String deptId) {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        Map<String, String> requestBody = new HashMap<>();
        requestBody.put("dept_id", deptId);
        Boolean hasMore = true;
        List<DingTalkUserVo> userVos = Lists.newArrayList();
        //游标
        int i = 0;
        while (hasMore) {
            requestBody.put("cursor", String.valueOf(i));
            //最大分页100
            requestBody.put("size", "100");
            log.info("[httpRequest][请求参数({})]", requestBody);
            // 发送请求
            HttpEntity<Map<String, String>> requestEntity = new HttpEntity<>(requestBody, headers);
            url = "https://oapi.dingtalk.com/topapi/v2/user/list";
            ResponseEntity<String> responseEntity = this.restTemplate.exchange(url + "?access_token=" + getToken(), HttpMethod.POST, requestEntity, String.class);
            log.debug("[httpRequest][的响应结果({})]", responseEntity);
            if (200 == responseEntity.getStatusCodeValue()) {
                JSONObject jsonObject = JSONObject.parseObject(responseEntity.getBody());
                String resultStr = jsonObject.getString("result");
                JSONObject resultObj = JSONObject.parseObject(resultStr);
                //是否还有更多
                hasMore = resultObj.getBoolean("has_more");
                String listStr = resultObj.getString("list");
                userVos.addAll(JSONArray.parseArray(listStr, DingTalkUserVo.class));
                if (!hasMore) {
                    //结束循环
                    break;
                }
                //正常返回的情况下才递增游标节点
                i++;
            }
        }
        return userVos;
    }


    @Transactional(rollbackFor = Exception.class)
    public void saveCollection(Collection<?> collection) {

        if (Objects.nonNull(collection)) {
            List<DingDept> deptList = Lists.newArrayList();
            List<DingUser> userList = Lists.newArrayList();
            //集合不为空
            collection.forEach(it -> {
                Class<?> aClass = it.getClass();
                if (ReflexaEnum.DING_DEPT.getCode().equals(aClass.getName())) {
                    //钉钉部门
                    DingTalkDeptVo dingTalkDeptVo = JSONObject.parseObject(JSONObject.toJSONString(it), DingTalkDeptVo.class);
                    List<DingTalkDeptVo> children = dingTalkDeptVo.getChildren();
                    if (CollectionUtils.isNotEmpty(children)) {
                       saveCollection(children);
                    }
                    DingDept dingDept = new DingDept();
                    BeanUtils.copyProperties(dingTalkDeptVo, dingDept);
                    dingDept.setDingDeptId(dingTalkDeptVo.getDept_id());
                    dingDept.setParentId(dingTalkDeptVo.getParent_id());
                    dingDept.setDeptId(StringPool.ONE);
                    dingDept.setDelFlag(StringPool.ZERO);
                    dingDept.setStatus(StringPool.ZERO);
                    log.info(dingDept.toString());
                    if (Objects.isNull(dingDeptService.getOne(Wrappers.<DingDept>lambdaQuery().eq(DingDept::getDingDeptId, dingDept.getDingDeptId())))) {
                        //存在的组织不增补
                        deptList.add(dingDept);
                    }
                }
                if (ReflexaEnum.DING_USER.getCode().equals(aClass.getName())) {
                    //钉钉用户
                    DingTalkUserVo dingTalkUserVo = JSONObject.parseObject(JSONObject.toJSONString(it), DingTalkUserVo.class);
                    DingUser dingUser = new DingUser();
                    BeanUtils.copyProperties(dingTalkUserVo, dingUser);
                    dingUser.setDingDeptIdList(dingTalkUserVo.getDept_id_list() != null ? Arrays.stream(dingTalkUserVo.getDept_id_list()).map(item -> String.valueOf(item)).collect(Collectors.joining(",")) : null);
                    dingUser.setDelFlag(StringPool.ZERO);
                    dingUser.setStatus(StringPool.ZERO);
                    if (Objects.isNull(dingUserService.getOne(Wrappers.<DingUser>lambdaQuery().eq(DingUser::getUserid, dingTalkUserVo.getUserid())))) {
                        //存在的组织不增补
                        userList.add(dingUser);
                    }
                }
            });

            if (CollectionUtils.isNotEmpty(deptList)) {
                //钉钉部门组织落表
                dingDeptService.saveBatch(deptList);
            }
            if (CollectionUtils.isNotEmpty(userList)) {
                //钉钉用户组织落表
                dingUserService.saveBatch(userList);
            }
        }
    }

    @Override
    public List<DingTalkDeptVo> getSyNcOrganizational() {
        List<DingTalkDeptVo> dingTalkDeptVos = departmentList();
        dingTalkDeptVos.stream().forEach(i -> {
            i.setUserList(getUserDepartmentList(i.getDept_id()));
        });
        return dingTalkDeptVos;
    }

    @Override
    public List<DingTalkDeptVo> getLocalList() {
        List<DingDept> list = dingDeptService.list(Wrappers.<DingDept>lambdaQuery().eq(DingDept::getStatus, StringPool.ZERO));
        List<DingTalkDeptVo> dingTalkDeptVos = Lists.newArrayList();
        for (DingDept d : list) {
            DingTalkDeptVo dingTalkDeptVo = new DingTalkDeptVo();
            dingTalkDeptVo.setId(d.getId());
            dingTalkDeptVo.setDept_id(d.getDingDeptId());
            dingTalkDeptVo.setParent_id(d.getParentId());
            dingTalkDeptVo.setName(d.getName());
            dingTalkDeptVo.setUserList(BeanMapperUtils.mapList(dingUserService.list(Wrappers.<DingUser>lambdaQuery().like(DingUser::getDingDeptIdList, d.getDingDeptId())), DingTalkUserVo.class));
            dingTalkDeptVos.add(dingTalkDeptVo);
        }
        return dingTalkDeptVos;
    }

    @Override
    public String getUserByPhone(String mobile) {
        if (StringUtils.isEmpty(mobile)) {
            mobile = Optional.ofNullable(SecurityUtils.getUserPhone()).orElseThrow(() -> new RuntimeException("获取用户电话号码异常"));
        }
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        Map<String, String> requestBody = new HashMap<>();
        requestBody.put("mobile", mobile);
        HttpEntity<Map<String, String>> requestEntity = new HttpEntity<>(requestBody, headers);
        // 发送请求
        ResponseEntity<String> responseEntity = this.restTemplate.exchange("https://oapi.dingtalk.com/topapi/v2/user/getbymobile?access_token=" + getToken(), HttpMethod.POST, requestEntity, String.class);
        log.debug("[httpRequest][的响应结果({})]", responseEntity);
        if (200 == responseEntity.getStatusCodeValue()) {
            JSONObject jsonObject = JSONObject.parseObject(responseEntity.getBody());
            String result = jsonObject.getString("result");
            JSONObject resultObj = JSONObject.parseObject(result);
            return Objects.nonNull(resultObj) ? resultObj.getString("userid") : null;
        }
        return null;
    }

    @Override
    public List<DingUser> getDingUserList(String query) {
        LambdaQueryWrapper<DingUser> queryWrapper = Wrappers.lambdaQuery();
        if (query != null && !query.isEmpty()) {
            // 判断query是否为手机号
            boolean isMobile = query.matches("^\\d+$");

            if (isMobile) {
                // 如果是手机号，进行手机号的模糊匹配
                queryWrapper.like(DingUser::getMobile, query);
            } else {
                // 如果不是手机号，进行姓名的模糊匹配
                queryWrapper.like(DingUser::getName, query);
            }
        }

        return dingUserMapper.selectList(queryWrapper);
    }

    /**
     * 自动派车
     *
     * @param instanceId
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void dispatchVehicle(String instanceId) {

        try {
            //根据instanceId查询车辆需求发起任务
            XhUseVehicleInfo xhUseVehicleInfo = xhUseVehicleInfoService.selectInfoByInstanceId(instanceId);
            if (Objects.nonNull(xhUseVehicleInfo)) {
                Long vehicleId = xhUseVehicleInfo.getId();

                // 判断redis中是否存在物流公司列表，没有->初始化
                if (!redisCache.hasKey("logisticsCompanies")) {
                    //查询所有物流公司id
                    List<Long> deptIdList = sysDeptService.selectChildrenDeptIdsById(110L).stream().map(SysDept::getDeptId).collect(Collectors.toList());
                    if (deptIdList != null && !deptIdList.isEmpty()) {
                        redisCache.setCacheList("logisticsCompanies", deptIdList);
                    }
                }
                //当前物流公司id
                Long deptId = getLogisticsCompany();
                //是否已经分派过
                XhVehicleOrderInfo checkOrderInfo =  xhVehicleOrderInfoService.selectVehicleOrderInfoByVehicleId(vehicleId);
                if (checkOrderInfo == null) {
                    //写入订单表主表
                    XhVehicleOrderInfo xhVehicleOrderInfo = new XhVehicleOrderInfo();
                    xhVehicleOrderInfo.setVehicleId(vehicleId);
                    xhVehicleOrderInfo.setDeptId(deptId);
                    xhVehicleOrderInfo.setStatus(XHConstant.ORDER_STATUS_WAIT);
                    xhVehicleOrderInfo.setCreateBy("系统");
                    int insert = xhVehicleOrderInfoService.insert(xhVehicleOrderInfo);
                    if (insert > 0) {
                        //写入订单副表
                        XhVehicleOrderHis xhVehicleOrderHis = new XhVehicleOrderHis();
                        xhVehicleOrderHis.setVehicleId(vehicleId);
                        xhVehicleOrderHis.setDeptId(deptId);
                        xhVehicleOrderHis.setCreateBy("系统");
                        xhVehicleOrderHis.setRemark("自动分派,待接收");
                        xhVehicleOrderHisService.insert(xhVehicleOrderHis);
                        //当前订单填入超时检查zSet
                        Date date = DateUtils.addHours(xhVehicleOrderInfo.getCreateTime(), 6);
                        scheduleOrderCheck(vehicleId, date);
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error("自动派单失败", e);
        }
    }

    /**
     * 获取物流公司
     */
    public Long getLogisticsCompany() {
        // 从Redis的List头部取出物流公司
        Long deptId = (Long) redisCache.getAndRotateFirstItem("logisticsCompanies");
        if (deptId != null) {
            // 将取出的物流公司放回List的尾部，实现轮询
            redisCache.appendItemToEnd("logisticsCompanies", deptId);
        }
        return deptId;
    }

    /**
     * 订单填入定时检查zSet
     */
    public Boolean scheduleOrderCheck(Long vehicleId, Date createTime) {
        // 将订单加入到待检查的队列中，设置检查时间为当前时间加上六小时
        Date date = DateUtils.addHours(createTime, 6);
        return redisCache.addItemToZSet("vehicleOrders", vehicleId, date.getTime());
    }

    /**
     * 定时任务 超时订单检查
     */
    @Override
    public void checkOrderStatuses() {
        Set<ZSetOperations.TypedTuple<Object>> vehicleOrders = redisCache.getAllItemsFromZSet("vehicleOrders");
        if (Objects.nonNull(vehicleOrders) && vehicleOrders.size()>0) {
            //当前时间
            Date nowDate = DateUtils.getNowDate();
            vehicleOrders.forEach(tuple -> {
                //受检查的任务id
                Long vehicleId = (Long) tuple.getValue();
                // redis中订单的超时时间
                Date date = new Date(tuple.getScore().longValue());

                XhVehicleOrderInfo xhVehicleOrderInfo = xhVehicleOrderInfoService.selectVehicleOrderInfoByVehicleId(vehicleId);
                if (Objects.nonNull(xhVehicleOrderInfo)) {
                    String status = xhVehicleOrderInfo.getStatus();

                    //订单状态
                    if (XHConstant.ORDER_STATUS_WAIT.equals(status)) {
                        if (nowDate.after(date)) {
                            //超时
                            //主表改状态
                            xhVehicleOrderInfo.setStatus(XHConstant.ORDER_STATUS_DEADLINE);
                            xhVehicleOrderInfoService.update(xhVehicleOrderInfo);

                            //从表插入
                            XhVehicleOrderHis xhVehicleOrderHis = new XhVehicleOrderHis();
                            xhVehicleOrderHis.setVehicleId(vehicleId);
                            xhVehicleOrderHis.setDeptId(xhVehicleOrderInfo.getDeptId());
                            xhVehicleOrderHis.setCreateTime(DateUtils.getNowDate());
                            xhVehicleOrderHis.setCreateBy(xhVehicleOrderInfo.getCreateBy());
                            xhVehicleOrderHis.setRemark("超时未接单");
                            xhVehicleOrderHisService.insert(xhVehicleOrderHis);
                            redisCache.removeItemFromZSet("vehicleOrders", vehicleId);
                            //TODO 发送系统消息 待修改 ···
//                            try {
//                                send("用车订单超时",null);
//                            } catch (Exception e) {
//                                throw new RuntimeException(e);
//                            }

                        }
                    } else {
                        redisCache.removeItemFromZSet("vehicleOrders", vehicleId);
                    }
                }

            });
        }
    }

    /**
     * 超时手动派单
     * @param id
     * @param username
     */
    @Override
    public void redirectVehicle(XhVehicleOrderInfoDTO xhVehicleOrderInfoDTO,String username) {

        try {
                //写入订单表主表
                XhVehicleOrderInfo xhVehicleOrderInfo = xhVehicleOrderInfoService.getInfo(xhVehicleOrderInfoDTO.getId());
                xhVehicleOrderInfo.setDeptId(xhVehicleOrderInfoDTO.getDeptId());
                xhVehicleOrderInfo.setStatus(XHConstant.ORDER_STATUS_WAIT);
                xhVehicleOrderInfo.setCreateBy(username);
                xhVehicleOrderInfo.setCreateTime(DateUtils.getNowDate());
                xhVehicleOrderInfo.setAssignType(xhVehicleOrderInfoDTO.getAssignType());
                boolean update = xhVehicleOrderInfoService.updateById(xhVehicleOrderInfo);
                if (update) {
                    //写入订单副表
                    XhVehicleOrderHis xhVehicleOrderHis = new XhVehicleOrderHis();
                    xhVehicleOrderHis.setVehicleId(xhVehicleOrderInfo.getVehicleId());
                    xhVehicleOrderHis.setDeptId(xhVehicleOrderInfo.getDeptId());
                    xhVehicleOrderHis.setCreateBy(username);
                    xhVehicleOrderHis.setRemark("重新分派,待接收");
                    xhVehicleOrderHisService.insert(xhVehicleOrderHis);
                    //当前订单填入超时检查zSet
                    Date date = DateUtils.addHours(xhVehicleOrderInfo.getCreateTime(), 6);
                    scheduleOrderCheck(xhVehicleOrderInfo.getVehicleId(), date);
                }
        } catch (Exception e) {
            e.printStackTrace();
            log.error("手动派单失败", e);
        }

    }

    public static Map<String, Object> toMap(Object obj) throws IllegalAccessException {
        Map<String, Object> map = new HashMap<>();
        Class<?> clazz = obj.getClass();
        for (Field field : clazz.getDeclaredFields()) {
            field.setAccessible(true); // 使得私有字段也可以访问
            map.put(field.getName(), field.get(obj));
        }
        return map;
    }

    @Override
    public String initiate(XhVehicleInfoDTO dto) {
        //获取用户钉钉id 默认提取当前登录人手机号
        String mobile = dto.getMobile();
        String dingUserId = getUserByPhone(mobile);
        if(Objects.isNull(dingUserId)){
            throw new RuntimeException("请到系统同步钉钉用户信息后重试!");
        }
//        String dingUserId = "2946351338773093";
//        String dingUserId = "25503248011229522";
        //转化
        XhUseVehicleInfo info = BeanMapperUtils.map(dto, XhUseVehicleInfo.class);
        info.setDingCreateBy(dingUserId);
        //默认备注里面放置钉钉实例id
        info.setIsAbnormalSum(StringPool.ZERO);
        info.setAbnormalSum(new BigDecimal("0.0"));
        info.setStatus(StringPool.ZERO);
        info.setCreateBy(mobile);
        xhUseVehicleInfoService.insert(info);
        Long id = info.getId();

        List<Map<String, Object>> mapList = Lists.newArrayList();
        if (Objects.nonNull(dto)) {
            Class<? extends XhVehicleInfoDTO> invokeClass = dto.getClass();
            Field[] fields = invokeClass.getDeclaredFields();
            try {
                for (Field field : fields) {
                    if (field.isAnnotationPresent(Excel.class)) {
                        field.setAccessible(true);
                        Excel declaredAnnotation = field.getDeclaredAnnotation(Excel.class);
                        String name = declaredAnnotation.name();
                        String fieldName = field.getName();
                        String dictType = declaredAnnotation.dictType();
                        Object objVal = field.get(dto);
                        if (Objects.nonNull(objVal)) {
                            Map<String, Object> map = new HashMap<>();
                            map.put("name", name);

                            if ("projectManager".equals(fieldName) || "dingtalkImgLinks".equals(fieldName) || "puckVehPeople".equals(fieldName)) {
                                objVal = Arrays.asList(objVal);
                            }

                            if("beginAddr".equals(fieldName)){
                                //始发地特殊处理
                                List<Object> list = Arrays.asList(objVal);
                                objVal = ((ArrayList) list.get(0)).stream().collect(Collectors.joining(","));
                            }

                            if ("deliverWay".equals(fieldName)
                                    || "eqType".equals(fieldName)
                                    || "isAbnormalSum".equals(fieldName)
                                    || "isQualityloss".equals(fieldName)
                                    || "isYaValue".equals(fieldName)
                            ) {
                                if (objVal instanceof Boolean) {
                                    Boolean b = (Boolean) objVal;
                                    if (b) {
                                        objVal = 1;
                                    } else {
                                        objVal = 0;
                                    }
                                }
                                objVal = sysDictDataService.selectDictLabel(dictType, String.valueOf(objVal));
                            }

                            if (objVal instanceof String) {
                                map.put("value", objVal);
                            } else {
                                map.put("value", JSONObject.toJSONString(objVal));
                            }
                            mapList.add(map);
                        }

                        if (StringUtils.isNotBlank(dictType)) {
                            //反射参考- 动态获取
//                            String fieldName = field.getName();
//                            Class<?> type = field.getType();
//                            Method setter = invokeClass.getMethod("set" + capitalize(fieldName), type);
//                            setter.invoke(DingTalkVehicleInfoDTO.class,field.getDeafultValue());
//                            Class<? extends DingTalkVehicleInfoDTO> aClass = dingTalkVehicleInfoDTO.getClass();
//                            Method getter = aClass.getMethod("get" + capitalize(fieldName));
//                            log.info((String) getter.invoke(DingTalkVehicleInfoDTO.class));
                        }

                    }
                }
                if (CollectionUtils.isNotEmpty(mapList)) {
                    HttpHeaders headers = new HttpHeaders();
                    headers.setContentType(MediaType.APPLICATION_JSON);
                    headers.set("x-acs-dingtalk-access-token", getToken());
                    // 请求体
                    Map<String, String> requestBody = new HashMap<>();
                    requestBody.put("deptId", "-1");
                    requestBody.put("processCode", processCode);
                    requestBody.put("originatorUserId", dingUserId);
                    requestBody.put("formComponentValues", JSONObject.toJSONString(mapList));
                    log.info("[httpRequest][请求参数({})]", requestBody);
                    // 发送请求
                    HttpEntity<Map<String, String>> requestEntity = new HttpEntity<>(requestBody, headers);
                    ResponseEntity<String> responseEntity = this.restTemplate.exchange("https://api.dingtalk.com/v1.0/workflow/processInstances", HttpMethod.POST, requestEntity, String.class);
                    log.debug("[httpRequest][的响应结果({})]", responseEntity);
                    String instanceId = JSONObject.parseObject(responseEntity.getBody()).getString("instanceId");

                    if (StringUtils.isNotBlank(instanceId)) {
                        info.setRemark(instanceId);
                        xhUseVehicleInfoService.updateById(info);
                        return String.valueOf(id);
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
                //异常则清除之前创建的用车申请
                xhUseVehicleInfoService.delete(id);
            }

        }
        return null;
    }


    private static String capitalize(String str) {
        return str.substring(0, 1).toUpperCase() + str.substring(1);
    }

    public static String underscoreToCamel(String input) {
        if (input == null) {
            return null;
        }
        String[] parts = input.split("_");
        StringBuilder camelCaseStr = new StringBuilder();
        for (String part : parts) {
            camelCaseStr.append(part.substring(0, 1).toUpperCase()).append(part.substring(1).toLowerCase());
        }
        return camelCaseStr.toString();
    }

    @Override
    public GetProcessInstanceResponseBody getWorkflowByProcessInstancesId(String processInstanceId) throws Exception {
        //拼接查询token
        com.aliyun.dingtalkworkflow_1_0.Client client = this.createClient();
        com.aliyun.dingtalkworkflow_1_0.models.GetProcessInstanceHeaders getProcessInstanceHeaders = new com.aliyun.dingtalkworkflow_1_0.models.GetProcessInstanceHeaders();
        getProcessInstanceHeaders.xAcsDingtalkAccessToken = getToken();
        com.aliyun.dingtalkworkflow_1_0.models.GetProcessInstanceRequest getProcessInstanceRequest = new com.aliyun.dingtalkworkflow_1_0.models.GetProcessInstanceRequest()
                .setProcessInstanceId(processInstanceId);
        try {
            GetProcessInstanceResponse response = client.getProcessInstanceWithOptions(getProcessInstanceRequest, getProcessInstanceHeaders, new com.aliyun.teautil.models.RuntimeOptions());
            return response.getBody();
        } catch (TeaException err) {
            if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
                // err 中含有 code 和 message 属性，可帮助开发定位问题
                log.error(err.message);
            }
        } catch (Exception _err) {
            TeaException err = new TeaException(_err.getMessage(), _err);
            if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
                // err 中含有 code 和 message 属性，可帮助开发定位问题
                log.error(err.message);
            }
        }
        return null;
    }

    /**
     * 动态创建合同
     *
     * @param request
     * @return
     * @throws FileNotFoundException
     */
    @Override
    public String createContract(CreateContractRequestVo request) throws FileNotFoundException {
        HashMap<String, String> map = new HashMap<>();
        map.put("wlgs", request.getWlgs());
        map.put("identificationNo", request.getIdentificationNo());
        map.put("phone", request.getPhone());
        map.put("begin", request.getBegin());
        map.put("end", request.getEnd());
        map.put("vehicleInfo", request.getVehicleInfo());
        map.put("peekDate", "(" + request.getPeekDate() + ")");
        map.put("pay", request.getPay() + "元");
        String payUpCase = MoneyUtils.change(Double.valueOf(request.getPay()));
        map.put("payUpCase", "( 大写：" + payUpCase + " )");
        String url = null;
        ;
        ByteArrayOutputStream pdfStream = PDFUtils.createPdfStream(ResourceUtils.getURL("classpath:").getPath() + "static/create.pdf", map);
        try {
            url = createUrl(pdfName, pdfStream);
            if (StringUtils.isNotBlank(url)) {
                url = url.replace("//", "/");
                log.info(url);
            }
        } catch (URISyntaxException e) {
            throw new RuntimeException(e);
        }
        return request.getUrl() + url;
    }

    public String createUrl(String filePath, ByteArrayOutputStream byteArrayOutputStream) throws URISyntaxException {
        try {

            byte[] pdfBytes = byteArrayOutputStream.toByteArray();
            MultipartFile multipartFile = new MockMultipartFile(
                    "file",
                    filePath,
                    "application/pdf",
                    pdfBytes
            );

            return FileUploadUtils.upload(multipartFile);
        } catch (Exception e) {
            log.error("创建Url时出错：" + e.getMessage());
        }
        return null;
    }

    @Override
    public List<MaterialShowTabVo> getUrlPathToMaterial(String type, String vehicleId) {
        List<XhVehicleInfoMaterial> list = materialService.list(Wrappers.<XhVehicleInfoMaterial>lambdaQuery()
                .eq(XhVehicleInfoMaterial::getVehicleId, vehicleId)
                .eq(XhVehicleInfoMaterial::getType, type)
        );
        return Optional.ofNullable(list).isPresent() ? BeanMapperUtils.mapList(list,MaterialShowTabVo.class) : null;
    }

    @Override
    public Boolean cancelWorkflowByProcessInstancesId(Map<String,String> cancelVO) throws Exception {
        com.aliyun.dingtalkworkflow_1_0.Client client = this.createClient();
        com.aliyun.dingtalkworkflow_1_0.models.TerminateProcessInstanceHeaders terminateProcessInstanceHeaders = new com.aliyun.dingtalkworkflow_1_0.models.TerminateProcessInstanceHeaders();
        terminateProcessInstanceHeaders.xAcsDingtalkAccessToken = getToken();
        com.aliyun.dingtalkworkflow_1_0.models.TerminateProcessInstanceRequest terminateProcessInstanceRequest = new com.aliyun.dingtalkworkflow_1_0.models.TerminateProcessInstanceRequest()
                .setProcessInstanceId(cancelVO.get("processInstanceId"))
                .setIsSystem(true)
                .setRemark(cancelVO.get("cancelReason"));
        //当isSystem为false时，该参数必传（需要传发起人才能撤销）
        //      .setOperatingUserId("133743186427339452");
        try {
            TerminateProcessInstanceResponse response = client.terminateProcessInstanceWithOptions(terminateProcessInstanceRequest, terminateProcessInstanceHeaders, new com.aliyun.teautil.models.RuntimeOptions());
            return response.getBody().result;
        } catch (TeaException err) {
            if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
                // err 中含有 code 和 message 属性，可帮助开发定位问题
                log.error(err.getMessage());
                err.printStackTrace();
            }

        } catch (Exception _err) {
            TeaException err = new TeaException(_err.getMessage(), _err);
            if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
                // err 中含有 code 和 message 属性，可帮助开发定位问题
                log.error(err.getMessage());
                err.printStackTrace();
            }

        }
        return false;
    }
    /**
     * 组合合同表单字段
     * @param dto
     */
    @Transactional(rollbackFor = Exception.class)
    public CreateContractRequestVo createContractForm(ContractFormDTO dto){
        //获取用车信息
        XhUseVehicleInfo vehicleInfo = dto.getVehicleInfo();
        XhCarTypeInfo carTypeInfo = dto.getCarTypeInfo();
        XhCarInfo carInfo = dto.getCarInfo();
        XhVehicleOrderInfo orderInfo = dto.getOrderInfo();
        List<XhDriverInfo> driverInfos = dto.getDriverInfos();
        CreateContractRequestVo createContractRequestVo = new CreateContractRequestVo();
        SysDept dept = sysDeptService.getOne(Wrappers.<SysDept>lambdaQuery().eq(SysDept::getDeptId, orderInfo.getDeptId()));
        createContractRequestVo.setIdentificationNo(dept.getIdentificationNo());
        createContractRequestVo.setWlgs(dept.getDeptName());
        createContractRequestVo.setPhone(dept.getAddr()+"/"+dept.getPhone());
        createContractRequestVo.setBegin(Arrays.asList(vehicleInfo.getBeginAddr()).stream().collect(Collectors.joining(",")));
        createContractRequestVo.setEnd(vehicleInfo.getEndAddr()+"/"+vehicleInfo.getEndContact());
        // 结算金额 公里数*车价
        createContractRequestVo.setPay(carTypeInfo.getPrice().multiply(vehicleInfo.getKilometre()).toString());
        //发货日期
        String deliverDate = vehicleInfo.getDeliverDate();
        //到货日期
        String needGiveDate = vehicleInfo.getNeedGiveDate();
        createContractRequestVo.setPeekDate( deliverDate +
                "--" +
                needGiveDate);
        StringBuilder sb = new StringBuilder();
        sb.append("车牌号: ");
        sb.append(carInfo.getCarNo());
//        sb.append("鲁GF815X");
        sb.append(System.lineSeparator());
        sb.append("车型: ");
        sb.append(carTypeInfo.getInfo());
        sb.append(System.lineSeparator());
        for (XhDriverInfo driverInfo : driverInfos) {
            sb.append("司机:");
            sb.append(driverInfo.getDriverName());
//            sb.append("西门吹雪");
            sb.append(" 电话: ");
            sb.append(driverInfo.getDriverPhone());
//            sb.append("18288886666");
            sb.append("");
            sb.append(System.lineSeparator());
            sb.append("身份证号: ");
            sb.append(driverInfo.getDriverIdCard());
//            sb.append("370702199502133321");
            sb.append(System.lineSeparator());
        }
        createContractRequestVo.setVehicleInfo(sb.toString());

        //保存到合同管理中
        //订单ID
        Long id = orderInfo.getId();
        XhContractInfo one = contractInfoService.getOne(Wrappers.<XhContractInfo>lambdaQuery().eq(XhContractInfo::getOrderId, id).eq(XhContractInfo::getStatus, StringPool.ZERO));
        if(Objects.isNull(one)){
            //新增
            XhContractInfo contractInfo = new XhContractInfo();
            contractInfo.setCarId(carInfo.getId());
            contractInfo.setOrderId(orderInfo.getId());
            contractInfo.setDeptId(dept.getDeptId());
            contractInfo.setHtStatus(Integer.valueOf(StringPool.ZERO));
            contractInfo.setBegin(createContractRequestVo.getBegin());
            contractInfo.setEnd(createContractRequestVo.getEnd());
            contractInfo.setPay(createContractRequestVo.getPay());
            contractInfo.setPayUpCase(MoneyUtils.change(Double.parseDouble(createContractRequestVo.getPay())));
            contractInfo.setVehicleInfo(createContractRequestVo.getVehicleInfo());
            contractInfo.setWlgs(createContractRequestVo.getWlgs());
            contractInfo.setDelFlag(StringPool.ZERO);
            contractInfo.setStatus(StringPool.ZERO);
            contractInfo.setWlgsInfo(createContractRequestVo.getPhone());
            contractInfo.setPeekDate(createContractRequestVo.getPeekDate());
            contractInfo.setIdentificationNo(createContractRequestVo.getIdentificationNo());
            contractInfo.setUpdateTime(DateUtils.getNowDate());
            contractInfoService.insert(contractInfo);
            createContractRequestVo.setId(contractInfo.getId());
        }else{
            one.setVehicleInfo(createContractRequestVo.getVehicleInfo());
            //重新上传
            one.setHtStatus(Integer.valueOf(XHConstant.STR_2));
            one.setUpdateTime(DateUtils.getNowDate());
            contractInfoService.updateById(one);
            createContractRequestVo.setId(one.getId());
        }

        return createContractRequestVo;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String downloadContract(Long id) {
        ContractFormDTO contractFormDTO = new ContractFormDTO();
        //查询订单
        XhVehicleOrderInfo orderInfo = xhVehicleOrderInfoService.getInfo(id);
        if(Objects.nonNull(orderInfo)){
            contractFormDTO.setOrderInfo(orderInfo);
            //查询用车需求
            contractFormDTO.setVehicleInfo(xhUseVehicleInfoService.getInfo(orderInfo.getVehicleId()));
            //查询车辆信息
            XhCarInfo carInfo = carInfoService.getInfo(orderInfo.getCarId());
            contractFormDTO.setCarInfo(carInfo);
            //查询车型信息
            contractFormDTO.setCarTypeInfo(carTypeInfoService.getInfo(Long.parseLong(carInfo.getCarType())));
            //司机信息
            /**判断订单表中司机是否存在
             * 如果存在则选择，如果不存在则取订单历史记录表最新的append_driver_info
             */
            String driverId = orderInfo.getDriverId();
            if(StringUtils.isNotBlank(driverId)){
                contractFormDTO.setDriverInfos(driverInfoService.list(Wrappers.<XhDriverInfo>lambdaQuery().in(XhDriverInfo::getId,Arrays.asList(driverId.split(",")))));
            }else{
                //最新一条历史记录
                XhVehicleOrderCarHis lastOne = carHisService.getOne(Wrappers.<XhVehicleOrderCarHis>lambdaQuery()
                        .eq(XhVehicleOrderCarHis::getVehicleId, orderInfo.getVehicleId())
                        .orderByDesc(XhVehicleOrderCarHis::getCreateTime).last("limit 1"));
                String appendDriverInfo = lastOne.getAppendDriverInfo();
                if(StringUtils.isNotBlank(appendDriverInfo)){
                    contractFormDTO.setDriverInfos(JSONArray.parseArray(appendDriverInfo, XhDriverInfo.class));
                }
            }
            //组装表单
            try {
                CreateContractRequestVo contractForm = createContractForm(contractFormDTO);
                contractForm.setUrl(downloadUrl);
                String downloadPdfUrl = createContract(contractForm);
                //根据订单id查询
                Long contractId = contractForm.getId();
                if(Objects.nonNull(contractId)){
                    XhContractInfo contractInfo = contractInfoService.getById(contractId);
                    contractInfo.setRemark(downloadPdfUrl);
                    contractInfoService.updateById(contractInfo);
                }
                return downloadPdfUrl;
            } catch (FileNotFoundException e) {
                throw new RuntimeException(e);
            }catch (Exception e){
                log.error("订单编号（{}）合同生成异常：{}",id,e.getMessage());
                throw new RuntimeException("合同生成失败！");
            }

        }
        return null;
    }

    @Override
    public void genQualityLoss(String instanceId,String type, String activityName) {
        //产生品损失
                try {
                    //根据实例id查询用车需求
                    XhUseVehicleInfo vehicleInfo = xhUseVehicleInfoService.getOne(Wrappers.<XhUseVehicleInfo>lambdaQuery().eq(XhUseVehicleInfo::getRemark, instanceId));
                    if (!Optional.ofNullable(vehicleInfo).isPresent()) {
                        log.error("发车需求-数据丢失!");
                    }
                    GetProcessInstanceResponseBody workflowByProcessInstancesId = getWorkflowByProcessInstancesId(instanceId);
                    //获取详情明细
                    GetProcessInstanceResponseBody.GetProcessInstanceResponseBodyResult result = workflowByProcessInstancesId.result;
                    //组件
                    List<GetProcessInstanceResponseBody.GetProcessInstanceResponseBodyResultFormComponentValues> formComponentValues = result.getFormComponentValues();
                    Stream<GetProcessInstanceResponseBody.GetProcessInstanceResponseBodyResultFormComponentValues> stream = formComponentValues.stream();
                    if (stream.anyMatch(it-> XHConstant.IS_QUALITYLOSS.equals(it.getName()) && XHConstant.YES.equals(it.getValue()))) {
                        //是否产生品质损失
                        formComponentValues.forEach(i->{
                            String name = i.getName();
                            String value = i.getValue();
                            if(XHConstant.QUALITYLOSS_FORM.equals(name)){
                                //品质损失清单
                                log.info(value);
                                List<Map> maps = JSONArray.parseArray(value, Map.class);
                                List<XhVehicleInfoMaterial> addList = Lists.newArrayList();
                                maps.forEach(m->{
                                    Object rowValue = m.get("rowValue");
                                    if (rowValue instanceof Collection) {
                                        List rowValueList = (List) rowValue;
                                        XhVehicleInfoMaterial xhVehicleInfoMaterial = new XhVehicleInfoMaterial();
                                        addList.add(xhVehicleInfoMaterial);
                                        //字典-material_type
                                        xhVehicleInfoMaterial.setType(XHConstant.STR_3);
                                        xhVehicleInfoMaterial.setDelFlag(StringPool.ZERO);
                                        xhVehicleInfoMaterial.setCreateTime(DateUtils.getNowDate());
                                        assert vehicleInfo != null;
                                        xhVehicleInfoMaterial.setVehicleId(vehicleInfo.getId());
                                        for (Object o : rowValueList) {
                                            if(o instanceof Map){
                                                Map<String,String> map = (Map) o;
                                                if (DingdingRowNameEnum.DING_DEVICE.getName().equals(map.get("label"))) {
                                                    //物料名称
                                                    xhVehicleInfoMaterial.setDevice(map.get("value"));
                                                }
                                                if (DingdingRowNameEnum.DING_SIZE.getName().equals(map.get("label"))) {
                                                    //型号
                                                    xhVehicleInfoMaterial.setSize(map.get("value"));
                                                }
                                                if (DingdingRowNameEnum.DING_COUNT.getName().equals(map.get("label"))) {
                                                    //数量
                                                    xhVehicleInfoMaterial.setCount(map.get("value"));
                                                }
                                                if (DingdingRowNameEnum.DING_UNIT.getName().equals(map.get("label"))) {
                                                    //金额
                                                    xhVehicleInfoMaterial.setUnit(map.get("value"));
                                                }
                                            }
                                        }
                                    }
                                });
                                //保存品质损失明细列表
                                materialService.insertBatch(addList);
                            }
                        });
                    }

                    //获取装车压车费、超长超宽费
                    if (stream.anyMatch(it-> XHConstant.IS_UN_NORMAL_FEE.equals(it.getName()) && XHConstant.YES.equals(it.getValue()))){
                        //查询订单信息
                        XhVehicleOrderInfo xhVehicleOrderInfo = xhVehicleOrderInfoService.lambdaQuery().eq(XhVehicleOrderInfo::getVehicleId,vehicleInfo.getId()).last(" limit 1").one();
                        List<XhVehicleOrderFee> feeList = new ArrayList<>();
                        //装车压车费
                        stream.filter(f->f.getName().equals(XHConstant.LOAD_PRESSURE_FEE)).findFirst().ifPresent(val->{
                            XhVehicleOrderFee xhVehicleOrderFee = new XhVehicleOrderFee();
                            xhVehicleOrderFee.setOrderId(xhVehicleOrderInfo.getId());
                            xhVehicleOrderFee.setVehicleId(vehicleInfo.getId());
                            xhVehicleOrderFee.setFeeMoney(val.getValue());
                            xhVehicleOrderFee.setType(XHConstant.STR_0);
                            feeList.add(xhVehicleOrderFee);
                        });
                        //超长超宽费
                        stream.filter(f->f.getName().equals(XHConstant.OUT_STAND_FEE)).findFirst().ifPresent(val->{
                            XhVehicleOrderFee xhVehicleOrderFee = new XhVehicleOrderFee();
                            xhVehicleOrderFee.setOrderId(xhVehicleOrderInfo.getId());
                            xhVehicleOrderFee.setVehicleId(vehicleInfo.getId());
                            xhVehicleOrderFee.setFeeMoney(val.getValue());
                            xhVehicleOrderFee.setType(XHConstant.STR_1);
                            feeList.add(xhVehicleOrderFee);
                        });
                        xhVehicleOrderFeeService.insertBatch(feeList);
                    }
                    log.info("{}",JSONObject.toJSONString(result));
                } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }


    @Override
    public String getPwScan(String code) {
        try {
            //https://login.dingtalk.com/oauth2/auth?redirect_uri=http://111.35.148.45:10002/api/dingtalk/test/login&response_type=code&client_id=dingkabnb6nulsczm0oz&scope=openid&prompt=consent
            //根据code获取登录用户信息
            com.aliyun.dingtalkoauth2_1_0.Client client = authClient();
            GetUserTokenRequest getUserTokenRequest = new GetUserTokenRequest()
                    //应用基础信息-应用信息的AppKey,请务必替换为开发的应用AppKey
                    .setClientId(appkey)
                    //应用基础信息-应用信息的AppSecret，,请务必替换为开发的应用AppSecret
                    .setClientSecret(appsecret)
                    .setCode(code)
                    .setGrantType("authorization_code");
            GetUserTokenResponse getUserTokenResponse = client.getUserToken(getUserTokenRequest);
            //获取用户个人token
            String userinfo = getUserinfo(getUserTokenResponse.getBody().getAccessToken());
            //判断是否有用户信息
            if(StringUtils.isNotBlank(userinfo)){
                JSONObject jsonObject = JSONObject.parseObject(userinfo);
                //获取钉钉用户的登录手机号
                String mobile = jsonObject.getString("mobile");
                SysUser user = userService.getOne(Wrappers.<SysUser>lambdaQuery().eq(SysUser::getUserName, mobile).eq(SysUser::getStatus, StringPool.ZERO).orderByAsc(SysUser::getCreateTime).last("limit 1"));
                if(!Optional.ofNullable(user).isPresent()){
                    //注册账号
                    SysUser sysUser = new SysUser();
                    sysUser.setDelFlag(StringPool.ZERO);
                    sysUser.setCreateTime(DateUtils.getNowDate());
                    sysUser.setUpdateTime(DateUtils.getNowDate());
                    sysUser.setUserName(mobile);
                    sysUser.setNickName(jsonObject.getString("nick"));
                    sysUser.setPhonenumber(mobile);
                    sysUser.setDeptId(100L);
                    sysUser.setCreateBy(mobile);
                    sysUser.setStatus(StringPool.ZERO);
                    sysUser.setRemark("钉钉虚拟用户");
                    //默认密码123456
                    sysUser.setPassword(SecurityUtils.encryptPassword("123456"));
                    sysUser.setEmail(mobile+"@qq.com");
                    //注册账号
                    userService.registerUser(sysUser);
                }
                return mobile;
            }
        } catch (Exception e) {
            log.error("获取用户信息失败：{}",e.getMessage());
            throw new RuntimeException("获取用户信息失败,请联系管理员！");
        }
        return null;
    }



    /**
     * 获取用户个人信息
     * @param accessToken
     * @return
     * @throws Exception
     */
    public String getUserinfo(String accessToken) throws Exception {
        com.aliyun.dingtalkcontact_1_0.Client client = contactClient();
        GetUserHeaders getUserHeaders = new GetUserHeaders();
        getUserHeaders.xAcsDingtalkAccessToken = accessToken;
        //获取用户个人信息，如需获取当前授权人的信息，unionId参数必须传me
        String me = JSON.toJSONString(client.getUserWithOptions("me", getUserHeaders, new RuntimeOptions()).getBody());
        System.out.println(me);
        return me;
    }

    @Override
    public String createPlan(DingTalkTodoDTO todoDTO) {
        try {
        com.aliyun.dingtalktodo_1_0.Client client = createTodoClient();
            CreateTodoTaskHeaders createTodoTaskHeaders = new CreateTodoTaskHeaders();
            createTodoTaskHeaders.xAcsDingtalkAccessToken = getToken();
            CreateTodoTaskRequest.CreateTodoTaskRequestNotifyConfigs notifyConfigs = new CreateTodoTaskRequest.CreateTodoTaskRequestNotifyConfigs()
                    .setDingNotify(String.valueOf(todoDTO.getDingNotify()));
            CreateTodoTaskRequest.CreateTodoTaskRequestContentFieldList contentFieldList0 = new CreateTodoTaskRequest.CreateTodoTaskRequestContentFieldList();
            CreateTodoTaskRequest.CreateTodoTaskRequestDetailUrl detailUrl = new CreateTodoTaskRequest.CreateTodoTaskRequestDetailUrl()
                    .setPcUrl(todoDTO.getPcUrl());
            CreateTodoTaskRequest createTodoTaskRequest = new CreateTodoTaskRequest()//当前操作者用户的unionId。
                    .setOperatorId(todoDTO.getCreateorId())
                    //业务系统侧的唯一标识ID，即业务ID
                    .setSourceId(String.valueOf(todoDTO.getSourceId()))
                    .setSubject(todoDTO.getSubject())
                    //发送人
                    .setCreatorId(todoDTO.getCreateorId())
                    //描述
                    .setDescription(todoDTO.getDescription())
                    //收件人
                    .setExecutorIds(todoDTO.getExecutorIds())
                    .setDetailUrl(detailUrl)
                    .setContentFieldList(java.util.Arrays.asList(
                            contentFieldList0
                    ))
                    .setIsOnlyShowExecutor(Boolean.TRUE)
                    .setPriority(todoDTO.getPriority())
                    .setNotifyConfigs(notifyConfigs);
            CreateTodoTaskResponse todoTaskWithOptions = client.createTodoTaskWithOptions(todoDTO.getCreateorId(), createTodoTaskRequest, createTodoTaskHeaders, new RuntimeOptions());
            return JSONObject.toJSONString(todoTaskWithOptions.getStatusCode());
            }catch (Exception _err) {
                TeaException err = new TeaException(_err.getMessage(), _err);
                if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
                    // err 中含有 code 和 message 属性，可帮助开发定位问题
                    log.error("创建待办失败:{}====={}", err.code, err.message);
                }
            }
            return null;
    }

    @Override
    public String getUnionid(String userId) {
        try {
            DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/v2/user/get");
            OapiV2UserGetRequest req = new OapiV2UserGetRequest();
            req.setUserid(userId);
            req.setLanguage("zh_CN");
            OapiV2UserGetResponse rsp = client.execute(req, getToken());
            System.out.println(rsp.getBody());
            OapiV2UserGetResponse.UserGetResponse result = rsp.getResult();
            return result.getUnionid();
        } catch (Exception e) {
           throw new RuntimeException(e);
        }
    }
}
